package ch.hslu.gl20vscpudemo;

import android.app.ActionBar;
import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;

public class GLES20vsCPUActivity extends Activity implements CameraSurfaceView.Callback, FrameBase.Config,
    FrameBase.Callback, OverlayView.Callback {

	// members ///////////////////////////////////////////////////////////////////
	private FrameLayout mActivitySurfaceRaw;
	private FrameLayout mActivitySurfaceResult;
	private FrameLayout mActivitySurfaceOverlay;

	// measurement
	private TextView mActivityTextMeasureView;
	private Handler mHandler;
	private long nMeaseredTimeMs;

	private CameraSurfaceView mCameraSurface;
	private OverlayView mOverlayView;
	private FrameBase mFrameBase;
	private Bitmap mBitmap;

	// configuration
	private Menu mMenu;
	private TextView mActivityTextConfigView;
	private FrameBase.Config.BitmapColor mFbConfBc;
	private FrameBase.Config.ProcessingUnit mFbConfPu;
	private int nKernelSize;
	private double dKernelSigma;

	// public methods ////////////////////////////////////////////////////////////

	/**
	 * Initialize the contents of the Activity's standard options menu.
	 * @param menu menu mebbers
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_oglespdemo, menu);
		mMenu = menu; // save menu member
		// default states
		mMenu.findItem(R.id.item_color_rgb).setChecked(true); // select menu
		mMenu.findItem(R.id.item_processingunit_gpu).setChecked(true);
		mMenu.findItem(R.id.item_config_kernel_7x7_2).setChecked(true);
		return true;
	}

	/**
	 * Called when the activity is starting.
	 * initialize:
	 *  - Surface source image
	 *  - Surface result image
	 *  - Overlay Surface (only touch needed)
	 *  - Measure Surface
	 *  - Config Surface
	 *  - Option menu bar
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		// set layout for app
		setContentView(R.layout.activity_gl20vscpudemo);

		// set layout
		mActivitySurfaceRaw = (FrameLayout) findViewById(R.id.raw_view);
		mActivitySurfaceResult = (FrameLayout) findViewById(R.id.result_view);
		mActivitySurfaceOverlay = (FrameLayout) findViewById(R.id.overlay_view);
		mActivityTextMeasureView = (TextView) findViewById(R.id.textmeasure_view);
		mActivityTextConfigView = (TextView) findViewById(R.id.textconfig_view);

		// handler for callback
		mHandler = new Handler();

		// create overlay
		mOverlayView = new OverlayView(this);
		mActivitySurfaceOverlay.addView(mOverlayView);

		// create preview view and set it as the content of this activity.
		mCameraSurface = new CameraSurfaceView(this);

		// install frame base
		mFrameBase = new FrameBase(this);

		// set default values
		mFbConfBc = FrameBase.Config.BitmapColor.RGB; // member
		mFbConfPu = FrameBase.Config.ProcessingUnit.GPU;
		nKernelSize = 7;
		dKernelSigma = 2.0;
		updateConfigAndConfigTextView();

		// update action bar
		ActionBar actionBar = getActionBar();
		actionBar.show();
	}

	/**
	 * Perform any final cleanup before an activity is destroyed
	 */
	@Override
	public void onDestroy() {
		super.onDestroy();
		mActivitySurfaceRaw = null;
		mActivitySurfaceResult = null;
		mCameraSurface = null;
		System.gc();
	}

	/**
	 * Called after onCreate(Bundle)
	 */
	@Override
	public void onStart() {
		super.onStart();
		mActivitySurfaceRaw.addView(mCameraSurface);
	}

	/**
	 * Called when you are no longer visible to the user.
	 */
	@Override
	public void onStop() {
		super.onStop();
		mActivitySurfaceRaw.removeAllViews();
		mActivitySurfaceOverlay.removeAllViews();
		mActivitySurfaceResult.removeAllViews();
	}

	/**
	 * Called after onStop() when the current activity is being re-displayed to the user
	 */
	@Override
	public void onRestart() {
		super.onRestart();
	}

	/**
	 * Called as part of the activity lifecycle when an activity is going into the background,
	 * but has not (yet) been killed. 
	 */
	@Override
	public void onPause() {
		super.onPause();
	}

	/**
	 * Called after onRestoreInstanceState(Bundle), onRestart(), or onPause()
	 */
	@Override
	public void onResume() {
		super.onResume();
	}

	/** CameraSurfaceView.Callback */
	public void onPictureTaken(byte[] data) {
		mFrameBase.pushImage(data);
		updateResultView();
	}

	/** OverlayView.Callback */
	public void onTouch() {
		mBitmap = mFrameBase.getBitmap();
		if(null != mBitmap){
		mFrameBase.pushImage(mBitmap);
		updateResultView();
		} else {
			Toast.makeText(this, R.string.text_no_src, Toast.LENGTH_SHORT).show();
		}
	}

	/** FrameBase.Callback */
	public void rendererTime(long timeMs) {
		Log.d(this.toString(), "rendertime: " + Long.toString(timeMs));
		nMeaseredTimeMs = timeMs;
		mHandler.post(new Runnable() {
			public void run() {
				// This gets executed on the UI thread so it can safely modify Views
				mActivityTextMeasureView.setText(" measured: " + Long.toString(nMeaseredTimeMs) + " ms");
			}
		});
	}

	/**
	 * is called whenever an item in options menu is selected.
	 * @param item The menu item that was selected.
	 * @return Return ture to consume it here
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		item.setChecked(true);
		// Handle item selection
		switch (item.getGroupId()) {
			case R.id.group_inputsrc:
				if (R.id.item_inputsrc_cam == item.getItemId()) {
					mActivitySurfaceRaw.removeAllViews();
					mActivitySurfaceRaw.addView(mCameraSurface);
					mMenu.findItem(R.id.item_autofocus).setEnabled(true); // enable autofocus menu
					// image is pushed by callback
				} else {
					// load source view
					mActivitySurfaceRaw.removeAllViews();
					mFrameBase.setGaussianKernel(1, 1.0);
					mFrameBase.setProcessingUnit(FrameBase.Config.ProcessingUnit.GPU);
					mFrameBase.pushImage(getResources().openRawResource(R.raw.square));
					mActivitySurfaceRaw.addView(mFrameBase.getSurface());
					mMenu.findItem(R.id.item_autofocus).setEnabled(false); // disable autofocus menu
					// force touch for result view
					onTouch();
				}
				break;
			case R.id.group_color:
				// config bitmap color
				mFbConfBc = (R.id.item_color_grey == item.getItemId() ? FrameBase.Config.BitmapColor.GREY
				    : FrameBase.Config.BitmapColor.RGB);
				break;
			case R.id.group_processingunit:
				// config processing unit
				mFbConfPu = (item.getItemId() == R.id.item_processingunit_gpu ? FrameBase.Config.ProcessingUnit.GPU : (item
				    .getItemId() == R.id.item_processingunit_cpu ? FrameBase.Config.ProcessingUnit.CPU
				    : FrameBase.Config.ProcessingUnit.CPUFPU));
				break;
			case R.id.group_config_kernel:
				if (250 < item.getOrder()) {
					nKernelSize = 21;
					dKernelSigma = 3;
				} else {
					nKernelSize = (int) (item.getOrder() >> 4);
					dKernelSigma = (double) (item.getOrder() & 0x0F);
				}
				break;
			default:
				if (R.id.item_autofocus == item.getItemId()) {
					// activate Autofocus
					mCameraSurface.setAutoFocus();
				} else {
					return false;
				}
				break;
		}
		updateConfigAndConfigTextView();
		return true;
	}

	// private methods ///////////////////////////////////////////////////////////
	/**
	 * update configuration and config the config-text-view
	 */
	private void updateConfigAndConfigTextView() {
		mFrameBase.setBitmapColor(mFbConfBc);
		if ((0 == nKernelSize) && (0 == dKernelSigma)) {
			int[][] customKernel =
				{
					    {
					        1, 1, 1
					    },
					    {
					        1, -8, 1
					    },
					    {
					        1, 1, 1
					    }
				};
			mFrameBase.setCustomKernel(customKernel);
		} else {
			mFrameBase.setGaussianKernel(nKernelSize, dKernelSigma);
		}
		mFrameBase.setProcessingUnit(mFbConfPu);
		String text = "color: "
		    + (FrameBase.Config.BitmapColor.RGB == mFbConfBc ? "RGB" : "GREY")
		    + "\n"
		    + "processing unit: "
		    + (FrameBase.Config.ProcessingUnit.GPU == mFbConfPu ? "GPU"
		        : (FrameBase.Config.ProcessingUnit.CPU == mFbConfPu ? "CPU" : "CPU_FPU")) + "\n";
		if (0 < nKernelSize) {
			text += "gaussian kernel size and sigma: " + Integer.toString(nKernelSize) + ", " + Double.toString(dKernelSigma);
		} else {
			text += "Laplacian";
		}
		mActivityTextConfigView.setText(text);
	}

	/**
	 * update result-view
	 */
	private void updateResultView() {
		mActivitySurfaceResult.removeAllViews();
		mActivitySurfaceResult.addView(mFrameBase.getSurface());
	}
}
